#include "http.h"

bool _sendStatus(const SOCKET clientSocket, const HTTPStatusCode code) {
    char buffer[32];
    sprintf(buffer, "HTTP/1.1 %d %s\r\n", code, getHTTPStatusMessage(code));
    return send(clientSocket, buffer, strlen(buffer), 0) == strlen(buffer);
}

bool _sendHeaders(const SOCKET clientSocket, HTTPHeaders *headers) {
    // 查询最长的header
    size_t maxSize = 0;
    for (size_t i=0; i<headers->length; i++) {
        const auto header = getHeaderByIdx(headers, i);
        const size_t size = strlen(header.key) + strlen(header.value);
        if (size > maxSize) maxSize = size;
    }
    // 发送头
    const char buffer[maxSize+16];
    for (int i = 0; i < headers->length; i++) {
        const auto header = getHeaderByIdx(headers, i);
        sprintf(buffer, "%s: %s\r\n", header.key, header.value);
        if (send(clientSocket, buffer, strlen(buffer), 0) != strlen(buffer)) return false;
    }
    // 发送空行表示头部结束
    return send(clientSocket, "\r\n", 2, 0) == 2;
}

bool _sendBinary(const SOCKET clientSocket, const char *binary, const unsigned long size) {
    if (binary == NULL) return false;
    return send(clientSocket, binary, size, 0) == SOCKET_ERROR;
}

bool _sendFile(const SOCKET clientSocket, FILE *file, const unsigned long offset, const unsigned long size) {
    if (file == NULL) return false;
    const int bufferSize = 65536;
    char buffer[bufferSize];
    unsigned long restSize = size;
    fseek(file, offset, SEEK_SET);
    while (true) {
        if (restSize <= 0) return true;
        const unsigned long needReadSize = restSize > bufferSize ? bufferSize : restSize;
        const long readSize = fread(buffer, sizeof(char), needReadSize, file);
        if (readSize <= 0) return false;
        if (send(clientSocket, buffer, readSize, 0) == SOCKET_ERROR) return false;
        restSize -= readSize;
    }
}

bool _sendResponse(const SOCKET clientSocket, HTTPResponse* response) {
    if (response == NULL) return false;
    if (!_sendStatus(clientSocket, response->code)) return false;
    if (!_sendHeaders(clientSocket, &response->headers)) return false;
    // 处理响应体
    if (response->_bodySize <= 0) return true;
    if (response->_useFileBody) {
        // 发送文件
        if (response->_fileBody == NULL) return false;
        return _sendFile(clientSocket, response->_fileBody, 0, response->_bodySize);
    }
    // 发送数据流
    if (response->_binaryBody == NULL) return false;
    return _sendBinary(clientSocket, response->_binaryBody, response->_bodySize);
}

typedef struct {
    SOCKET clientSocket;
    Route *routes;
    size_t routeCount;
} HTTPContext;

void httpHandler(PTP_CALLBACK_INSTANCE instance, const PVOID context, const PTP_WORK work) {
    const auto ctx = (HTTPContext*)context;
    const SOCKET clientSocket = ctx->clientSocket;
    const Route *routes = ctx->routes;
    const size_t routeCount = ctx->routeCount;

    HTTPRequest request = newEmptyRequest();
    HTTPResponse response = newEmptyResponse();
    while (true) {
        // 清空原来的数据，并初始化一些值
        makeEmptyRequest(&request);
        makeEmptyResponse(&response);
        // 读取一个请求
        if(!recvHTTPRequestFromSocket(clientSocket, &request)) {
            response.code = HTTP_BAD_REQUEST;
            _sendResponse(clientSocket, &response);
            break;
        }
        // 根据路由表处理请求和响应
        bool sentResponse = false;
        for(int i=0; i<routeCount; i++) {
            const Route route = routes[i];
            if (strstr(route.method, request.method) == NULL) continue;
            if (route.fullMatch && (strcmp(request.path, route.path) != 0)) continue;
            if (!route.fullMatch && strncmp(request.path, route.path, strlen(route.path)) != 0) continue;
            route.handler(&request, &response);
            _sendResponse(clientSocket, &response);
            sentResponse = true;
            break;
        }
        if(!sentResponse) {
            response.code = HTTP_NOT_FOUND;
            _sendResponse(clientSocket, &response);
        }
        // 记录日志
        printf("%d %s %s\n", response.code, request.method, request.url);
    }

    freeHTTPRequest(&request);
    freeHTTPResponse(&response);
    free(ctx);
    closesocket(clientSocket);
    CloseThreadpoolWork(work);
}

void runHTTPServer(
    const SOCKET serverSocket, Route *routes,
    const size_t routeCount,
    const unsigned int minThreadCount,
    const unsigned int maxThreadCount
) {
    // 初始化线程池
    TP_CALLBACK_ENVIRON env;
    InitializeThreadpoolEnvironment(&env);

    // 创建线程池
    const PTP_POOL pool = CreateThreadpool(NULL);
    if (pool == NULL) {
        printf("create thread pool failed\n");
        return;
    }
    SetThreadpoolThreadMinimum(pool, 1);
    SetThreadpoolThreadMaximum(pool, maxThreadCount);
    SetThreadpoolCallbackPool(&env, pool);

    while (true) {
        // 接受客户端连接
        struct sockaddr_in clientAddr;
        int clientSize = sizeof(clientAddr);
        SOCKET clientSocket = INVALID_SOCKET;
        clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &clientSize);
        if (clientSocket == INVALID_SOCKET) {
            printf("Accept failed: %d\n", WSAGetLastError());
            break;
        }
        // 创建工作项
        auto context = (HTTPContext*)malloc(sizeof(HTTPContext));
        if (context == NULL) {
            printf("create context error: Memory allocation failed\n");
            closesocket(clientSocket);
            continue;
        }
        context->clientSocket = clientSocket;
        context->routes = routes;
        context->routeCount = routeCount;
        const PTP_WORK work = CreateThreadpoolWork(httpHandler, context, &env);
        if (work == NULL) {
            printf("create thread pool work failed\n");
            closesocket(clientSocket);
            continue;
        }
        // 提交工作项到线程池
        SubmitThreadpoolWork(work);
    }

    CloseThreadpool(pool);
    DestroyThreadpoolEnvironment(&env);
}